package xlog

import (
	"errors"
	"fmt"
	"os"
	"runtime/debug"

	"github.com/gookit/color"
)

// Fmt 帮助规范化log的格式， 通常使用`xlog.Errorf(xlog.Fmt(), xlog.Args(), err)`来包装错误即可
func Fmt(s ...string) string {
	if len(s) == 0 {
		return "%v\n%w"
	}
	return "%v|" + s[0] + "\n%w"
}

// Error 只能输出一个error对象
func Error(err error, exitCode ...int) {
	if err == nil {
		return
	}
	file, line, name := fileAndLine(false)
	errString := fmt.Sprintf("[EROR] %v:%v %v \n%v\n", file, line, name, err)
	errLog.Print(color.BgRed.Render(errString))
	if len(exitCode) > 0 {
		os.Exit(exitCode[0])
	}
}

// func Error(err error, exitCode ...int) {
// 	if err == nil {
// 		return
// 	}
// 	stack := debug.Stack()
// 	file, line, name := fileAndLine(false)
// 	errString := fmt.Sprintf("[ERR] %v:%v %v \n%v\n%s", file, line, name, err, stack)
// 	errLog.Print(color.Red.Render(errString))
// 	if len(exitCode) > 0 {
// 		os.Exit(exitCode[0])
// 	}
// }

// Errorf 用来取代fmt.Errorf(), 相比于fmt的实现, 增加了代码行数和调用函数的输出
func Errorf(format string, args ...interface{}) error {
	file, line, name := fileAndLine(false)
	args = append([]interface{}{file, line, name}, args...)
	return fmt.Errorf("%v:%v %v "+format, args...)
}

// TypeErrorf 相比于Errorf，增加了一个错误码用于描述错误类型
func TypeErrorf(typ int, format string, args ...interface{}) error {
	file, line, name := fileAndLine(false)
	args = append([]interface{}{file, line, name}, args...)
	return &typeError{
		Type: typ,
		Err:  fmt.Errorf("%v:%v %v "+format, args...),
	}
}

// typeError 包裹了一个类型参数，用来给错误分类
type typeError struct {
	Type int
	Err  error
}

func (e *typeError) Error() string {
	return fmt.Sprintf("T:%v %v", e.Type, e.Err)
}

func (e *typeError) Unwrap() error {
	return e.Err
}

func GetErrorType(err error) int {
	if err == nil {
		return 0
	}
	for {
		if e, ok := err.(*typeError); ok {
			return e.Type
		}
		err = errors.Unwrap(err)
		if err == nil {
			return 0
		}
	}
}

func Recover(e *error) {
	if *e != nil {
		return
	}
	if err1 := recover(); err1 != nil {
		if err, ok := err1.(error); ok {
			*e = fmt.Errorf("panic: %w\n%s", err, debug.Stack())
		} else {
			*e = fmt.Errorf("panic2: %w\n%s", err, debug.Stack())
		}
	}
}
